/**
 * Copyright (c) suzuki ryoutoku
 * See this code regarding the Apache License Version 2.0
 * @author suzuki ryoutoku
 */


//=============================================================================
//	Protected multiple include file
//=============================================================================
#ifndef READER_H
#define READER_H

//=============================================================================
//	Preprocessor
//=============================================================================
#include "include/declare.h"
#include "mathematics/Mathematics.h"
#include <fstream>
#include <vector>
#include <stack>
using namespace std;

//=============================================================================
//	declare local class & function
//=============================================================================
namespace library{
  /**
   * Reader of objfile use these classes.
   */
  namespace objfile{
	class ObjGroup;
	class ObjVolume;
  }

  /**
   * Reader of 3ds use these classes & function 
   */
  namespace max{
	class Object;
	class FaceGroup;
	class Material;
	class Chunk;
	unsigned int getName(FILE* fp, char* name);
  }
}

//=============================================================================
//	Define enum type
//=============================================================================
/**
 * 3DS file's chunk id
 * \note
 * Sign EDIT3DS is to express starting actually data.
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-5
 */
enum StudioMaxChunk{
  EDIT3DS           = 0x3D3D,
  AMBIENT_LIGHT     = 0X2100,
  OBJECT            = 0x4000,
  OBJECT_MESH       = 0x4100,
  MESH_VERTICES     = 0x4110,
  MESH_FACES        = 0x4120,
  MESH_TEXTURE_COORD= 0x4140,
  MESH_MATERIAL     = 0x4130,
  MATERIAL          = 0xAFFF,
  MATERIAL_NAME     = 0xA000,
  MATERIAL_DIFFUSE  = 0xA020,
  MATERIAL_TEXMAP   = 0xA200,
  MATERIAL_TEXFILE  = 0xA300,
};

//=============================================================================
//	Define class
//=============================================================================
/**
 * This class is super class for all classes reding 3d data file.
 * @author suzuki ryoutoku 
 * @date 2007-10-14
 * @version 1.0
 */
class library::graphics::Reader
{
  // closed member
protected:
  ifstream 	           				m_fin;//!< handle of pcm file
  int 	   					 m_sum_vertex;//!< vertex sum in file
  int 	   	 				 m_sum_volume;//!< volume sum in file
  int	   	 			  m_sum_innernode;//!< inside node sum
  int      				 m_sum_outerpatch;//!< outside patch sum
  int      				 m_sum_innerpatch;//!< inside patch sum
  int 	   	 			  m_sum_inneredge;//!< inside edge sum
  int 	   	 			  m_sum_outeredge;//!< outside edge sum
  char	   	 			  m_filename[256];//!< file name
  library::log::LogManager*	m_log_manager;

  // opened class member
public:
  const static int  NumberOfCharactersLine;
  const static char* Comment;

  // opened method
public:
  Reader(library::log::LogManager* arg);
  ~Reader();
  
  library::graphics::Model* readPCH(const string& filename);

  // virtual opened method
public:
  virtual bool isRequiredCreatingImageDatas(void);
  virtual void createImageDatas(Model* arg, ImageDataManager* imd);

  // closed method
protected:
  bool openFile(const string& filename);
  bool readVertexInfomation(void);
  bool readVertex(Vector<double>* arg);
  bool jumpVertexInPCMFile(void);
  bool readPatchInfomation(void);
  bool readOuterPatch(int* arg);

  bool allocateVolume(Model* arg);
  bool allocateVertex(Model* arg);

  // inline setter & getter
public:
  char* getM_filename(void){return this->m_filename;}

  // open class method
public:
  static bool CheckLine(string* arg);
  static int CountData(const string& arg, const string specify = " " );
  static void ExtractModelName(const string& arg, char* model);
};



/**
 * This class allow you to load a pcm file (it's adventure project format).
 * \note
 * [Usage]
 * 1. You create instance of this class.
 * 2. You call this class's readPCMFile method, then argument must be pcm 
 * file name.
 *
 * @author suzuki ryoutoku 
 * @date 2007-10-14
 * @version 1.0
 */
class library::graphics::ReadPcm 
  : public library::graphics::Reader
{
  // closed member
private:
  int*		m_face_group_index;
  int		m_face_sum;

  // opened method
public:
  ReadPcm(library::log::LogManager* arg);
  ~ReadPcm();
  
  library::graphics::Model* readPCMFile(const string& filename);

  // closed method
private:
  int readPCGFile(const string& filename);
  int readFaceGroup(ifstream& fin, int group_index, int face_sum);
  int readFaceSumInPCMFile(const string& filename);
  bool jumpVertex(void);

  int searchFaceGroupIndex(int face_index);

};


/**
 * This class allow you to load a 3ds file (it's for 3D Studio max).
 * \note
 * 3ds is dedicated file of 3D Studio Max.
 * When I writing this class, Following url is used as reference.
 * http://www.spacesimulator.net/tut4_3dsloader.html
 * [Usage]
 * 1. You create instance of this class.
 * 2. You call this class's read3DSFile method, then argument must be 3ds 
 * file name.
 *
 * @author suzuki ryoutoku 
 * @date 2008-1-2
 * @version 1.0
 */
class library::graphics::Read3ds
  : public library::graphics::Reader
{
  // closed member
private:
  vector<library::max::Object*>	   m_objects;/**<
												A Object corresponding a volume
												in library::graphics::Model
											 */
  stack<library::max::Object*>	   m_cobject;/**<
												This stack is current mesh,
												Mesh data is since recursive 
												in 3ds file.
											 */
  vector<library::max::Material*> m_material;
  stack<library::max::Material*> m_cmaterial;


  // opened method
public:
  Read3ds(library::log::LogManager* arg);
  ~Read3ds();
  library::graphics::Model* read3DS(const string& filename);

  // inline setter & getter
public:
  vector<library::max::Material*>* getM_material(void){return &m_material;}

  // closed methods
private:
  void readMesh(FILE* fp, library::max::Chunk* head);
  void readMeshVertices(FILE* fp, library::max::Chunk* head);
  void readMeshFaces(FILE* fp, library::max::Chunk* head);
  void readMeshMaterial(FILE* fp, library::max::Chunk* head);
  void readMeshTextureCoord(FILE* fp, library::max::Chunk* head);
  void readObject(FILE* fp, library::max::Chunk* head);
  void readDiffuse(FILE* fp, library::max::Chunk* head);
  void readMaterial(FILE* fp, library::max::Chunk* head);
  void readEdit3ds(FILE* fp, library::max::Chunk* head);

  Model* createModelData(void);

  unsigned int getMaterialIndex(const char* name) throw (char*);

};


/**
 * The class is header of chunk in 3ds file.
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-5
 */
class library::max::Chunk
{
public:
  unsigned short m_ID;
  unsigned int m_length;
  unsigned int m_bytesread;

public:
  Chunk();
  void display(void);
  unsigned int readFile(FILE* fp);
  unsigned int skipFile(FILE* fp);

public:
  static unsigned int DATASIZE;
};

/**
 * The class express Object data in 3d studio max.
 * @author suzuki ryoutoku 
 * @date 2008-1-2
 * @version 1.0
 */
class library::max::Object
{
  // closed member
private:
  char					 		 m_name[20];//!< Length of name in max is 20.
  unsigned int					m_vertexsum;
  vector<float>		     			m_coord;
  unsigned int		  		  m_trianglesum;
  vector<unsigned short>			m_index;
  unsigned int						m_uvsum;
  vector<float>						   m_uv;
  vector<library::max::FaceGroup*>	m_group;
  int		 		 m_last_total_vertexsum;/**< 
											   when creating model data, 
											   it's used.
											*/
  
  // opened method
public:
  Object();
  ~Object();
  bool isModelData(void);
  unsigned int queryGroupSum(void);

  // inline setter & getter
public:
  void setM_name(char* arg){strcpy(m_name,arg);}

  // friend class
public:
  friend class library::graphics::Read3ds;
};

/**
 * This class roles Material infomations in 3ds file.
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-20
 */
class library::max::Material
{
  // closed member
private:
  char				m_name[20];
  unsigned char	  m_diffuse[3];
  char	m_texture_filename[20];

  // opened method
public:
  Material();
  ~Material();

  // inline setter & getter
public:
  void setM_name(char* arg){strcpy(m_name,arg);}

  void setM_texture_filename(char* arg){strcpy(m_texture_filename,arg);}
  char* getM_texture_filename(void){return m_texture_filename;}
  
public:
  friend class library::graphics::Read3ds;
};


/**
 * @author suzuki ryoutoku
 * @version 1.0
 * @date 2008-2-15
 */
class library::max::FaceGroup
{
  // closed member
private:
  char		   m_materialname[20];
  vector<unsigned short>  m_faces;
  
  // opened method
public:
  FaceGroup();
  ~FaceGroup();

  // inline setter & getter
public:
  void setM_materialname(char* arg){strcpy(m_materialname,arg);}

public:
  friend class library::graphics::Read3ds;
  friend class library::max::Object;
};

/**
 * Reading the obj format.
 * Following text is to explain this class's member correspond 
 * with name of data chunk in obj format.
 *
 * The Volume correspond with the group in obj format.
 *
 * The m_group correspond with the region specified usemtl keyword in the 
 * group in obj format.
 *
 * I think no need is to explain vertex and connectivity...
 *
 * @see http://www.eg-models.de/formats/Format_Obj.html
 * @author suzuki ryoutoku 
 * @date 2008-1-1
 * @version 1.0
 */
class library::graphics::ReadObj
  : public library::graphics::Reader
{
  // closed member
private:
  vector<library::mathematics::Vector<double>*>	m_vertex;//!< vertex data
  vector<library::objfile::ObjVolume*>		 m_objvolume;/**<
															A ObjVolume 
															corresponding 
															a volume 
															in graphics::Model
														 */
  library::objfile::ObjVolume*		 m_current_objvolume;/**<
															When load a file,
															reading data is 
															stored this member.
														 */
  
  // opened method
public:
  ReadObj(library::log::LogManager* arg);
  ~ReadObj();

  library::graphics::Model* readOBJFile(const string& filename) throw (char*);

  // closed member
private:
  unsigned int evalLine(const string& arg);
  bool setCurrentVolume(const char* arg);
  bool setFaceInCurrentGroup(const string& arg);


  // enum
public:
  enum LineSymbol{
	NULL_LINE	= 0x00,//!< no handle
	VERTEX		= 0x01,//!< vertex infomation line
	GROUP		= 0x02,//!< Under line in file is belong to this group
	USEMATERIAL	= 0x03,//!< Under line in file is belong to this material
	FACE		= 0x04,//!< face infomation line
  };

};

/**
 * The class is corresponding library::graphics::Group.
 * @author suzuki ryoutoku
 * @date 2008-1-1
 * @version 1.0
 */
class library::objfile::ObjGroup
{
  // closed member
private:
  char						      m_keyword[256];
  int								   m_tri_sum;
  int								  m_quad_sum;
  vector<library::graphics::Polygon*> m_objfaces;

  // opened method
public:
  ObjGroup(const char* name);
  ~ObjGroup();
  // firend class
public:
  friend class library::graphics::ReadObj;
  friend class library::objfile::ObjVolume;
};

/**
 * The class is corresponding a library::graphics::Volume.
 * The class have one or more library::objfile::ObjGroup.
 * @author suzuki ryoutoku
 * @date 2008-1-1
 * @version 1.0
 */
class library::objfile::ObjVolume
{
  // opened member
public:
  char		   					   m_groupname[256];
  vector<library::objfile::ObjGroup*>	m_objgroups;
  library::objfile::ObjGroup*	 m_current_objgroup;
  // opened method
public:
  ObjVolume(const char* arg);
  ~ObjVolume();
  unsigned int countEnableGroup(void);
  bool setCurrentUseMaterial(const char* arg);
};


#endif
